﻿using System;
using System.Diagnostics.CodeAnalysis;

namespace BrightSword.SwissKnife
{
    [ExcludeFromCodeCoverage]
    // reference implementation of a monad
    internal class M<T>
    {
        private readonly T _value;

        public M(T value)
        {
            _value = value;
        }

        // UNIT
        public static implicit operator M<T>(T value)
        {
            return new M<T>(value);
        }

        // BIND
        public M<U> Bind<U>(Func<T, M<U>> func)
        {
            return func(_value);
        }

        // UGLY BIND
        public void Bind(Action<T> action)
        {
            action(_value);
        }
    }

    [ExcludeFromCodeCoverage]
    internal class Nullable<T>
        where T : class
    {
        private readonly T _value;

        private Nullable(T value)
        {
            _value = value;
        }

        // UNIT
        public static implicit operator Nullable<T>(T value)
        {
            return new Nullable<T>(value);
        }

        // BIND
        public Nullable<U> Maybe<U>(Func<T, Nullable<U>> func) where U : class
        {
            return _value == null
                       ? null
                       : func(_value);
        }
    }

    [ExcludeFromCodeCoverage]
    internal static class Nullable
    {
        public static Nullable<U> Maybe<T, U>(this T value,
                                              Func<T, Nullable<U>> func) where T : class where U : class
        {
            return value == null
                       ? null
                       : func(value);
        }
    }

    [ExcludeFromCodeCoverage]
    internal class Black<TCash>
    {
        private readonly TCash _value;

        private Black(TCash value)
        {
            _value = value;
        }

        public static implicit operator Black<TCash>(TCash value)
        {
            return new Black<TCash>(value);
        }

        public Black<TSomething> Buy<TSomething>(Func<TCash, Black<TSomething>> shopForAnythingFunc)
        {
            return shopForAnythingFunc(_value);
        }
    }
}